﻿using Microsoft.Extensions.Caching.Distributed;
using Net8.Project.Common.Extensions;
using System.Text;

namespace Net8.Project.Common.Caches
{
    /// <summary>
    /// 缓存服务接口，基于 IDistributedCache 封装，提供常用缓存操作方法。
    /// </summary>
    public class Caching : ICaching
    {
        private readonly IDistributedCache _cache;
        public Caching(IDistributedCache cache)
        {
            _cache = cache;
        }

        public IDistributedCache Cache => _cache;

        private static byte[] GetBytes<T>(T source)
        {
            return Encoding.UTF8.GetBytes(source.ToJson());
        }

        public void AddCacheKey(string cacheKey)
        {
            var res = _cache.GetString(CacheConst.KeyAll);
            var allkeys = string.IsNullOrWhiteSpace(res) ? new List<string>() : res.ToObject<List<string>>();
            if (!allkeys.Any(m => m == cacheKey))
            {
                allkeys.Add(cacheKey);
                _cache.SetString(CacheConst.KeyAll, allkeys.ToJson());
            }
        }

        public async Task AddCacheKeyAsync(string cacheKey)
        {
            var res = await _cache.GetStringAsync(CacheConst.KeyAll);
            var allkeys = string.IsNullOrWhiteSpace(res) ? new List<string>() : res.ToObject<List<string>>();
            if (!allkeys.Any(m => m == cacheKey))
            {
                allkeys.Add(cacheKey);
                await _cache.SetStringAsync(CacheConst.KeyAll, allkeys.ToJson());
            }
        }

        #region Get
        public bool Exists(string cacheKey)
        {
            var res = _cache.Get(cacheKey);
            return res != null;
        }

        public async Task<bool> ExistsAsync(string cacheKey)
        {
            var res = await _cache.GetAsync(cacheKey);
            return res != null;
        }

        public List<string> GetAllCacheKeys()
        {
            var res = _cache.GetString(CacheConst.KeyAll);
            return string.IsNullOrWhiteSpace(res) ? null : res.ToObject<List<string>>();
        }

        public async Task<List<string>> GetAllCacheKeysAsync()
        {
            var res = await _cache.GetStringAsync(CacheConst.KeyAll);
            return string.IsNullOrWhiteSpace(res) ? null : res.ToObject<List<string>>();
        }

        public T Get<T>(string cacheKey)
        {
            var res = _cache.Get(cacheKey);
            return res == null ? default : Encoding.UTF8.GetString(res).ToObject<T>();
        }

        public async Task<T> GetAsync<T>(string cacheKey)
        {
            var res = await _cache.GetAsync(cacheKey);
            return res == null ? default : Encoding.UTF8.GetString(res).ToObject<T>();
        }

        public object Get(Type type, string cacheKey)
        {
            var res = _cache.Get(cacheKey);
            return res == null ? default : Encoding.UTF8.GetString(res).ToObject(type);
        }

        public async Task<object> GetAsync(Type type, string cacheKey)
        {
            var res = await _cache.GetAsync(cacheKey);
            return res == null ? default : Encoding.UTF8.GetString(res).ToObject(type);
        }

        public string GetString(string cacheKey)
        {
            return _cache.GetString(cacheKey);
        }

        public async Task<string> GetStringAsync(string cacheKey)
        {
            return await _cache.GetStringAsync(cacheKey);
        } 
        #endregion

        #region Remove
        public void Remove(string key)
        {
            _cache.Remove(key);
            DelCacheKey(key);
        }

        public async Task RemoveAsync(string key)
        {
            await _cache.RemoveAsync(key);
            await DelCacheKeyAsync(key);
        }

        public void RemoveAll()
        {
            var catches = GetAllCacheKeys();
            foreach (var @catch in catches) Remove(@catch);

            catches.Clear();
            _cache.SetString(CacheConst.KeyAll, catches.ToJson());
        }

        public async Task RemoveAllAsync()
        {
            var catches = await GetAllCacheKeysAsync();
            foreach (var @catch in catches) await RemoveAsync(@catch);

            catches.Clear();
            await _cache.SetStringAsync(CacheConst.KeyAll, catches.ToJson());
        } 
        #endregion

        #region Set
        public void Set<T>(string cacheKey, T value, TimeSpan? expire = null)
        {
            _cache.Set(cacheKey, GetBytes(value),
                expire == null
                    ? new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(6) }
                    : new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = expire });

            AddCacheKey(cacheKey);
        }

        public async Task SetAsync<T>(string cacheKey, T value)
        {
            await _cache.SetAsync(cacheKey, Encoding.UTF8.GetBytes(value.ToJson()),
                new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(6) });

            await AddCacheKeyAsync(cacheKey);
        }

        public async Task SetAsync<T>(string cacheKey, T value, TimeSpan expire)
        {
            await _cache.SetAsync(cacheKey, Encoding.UTF8.GetBytes(value.ToJson()),
                new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = expire });

            await AddCacheKeyAsync(cacheKey);
        }

        public void SetPermanent<T>(string cacheKey, T value)
        {
            _cache.Set(cacheKey, Encoding.UTF8.GetBytes(value.ToJson()));
            AddCacheKey(cacheKey);
        }

        public async Task SetPermanentAsync<T>(string cacheKey, T value)
        {
            await _cache.SetAsync(cacheKey, Encoding.UTF8.GetBytes(value.ToJson()));
            await AddCacheKeyAsync(cacheKey);
        }

        public void SetString(string cacheKey, string value, TimeSpan? expire = null)
        {
            if (expire == null)
                _cache.SetString(cacheKey, value, new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(6) });
            else
                _cache.SetString(cacheKey, value, new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = expire });

            AddCacheKey(cacheKey);
        }

        public async Task SetStringAsync(string cacheKey, string value)
        {
            await _cache.SetStringAsync(cacheKey, value, new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(6) });

            await AddCacheKeyAsync(cacheKey);
        }

        public async Task SetStringAsync(string cacheKey, string value, TimeSpan expire)
        {
            await _cache.SetStringAsync(cacheKey, value, new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = expire });

            await AddCacheKeyAsync(cacheKey);
        }

        public async Task SetMaxDataScopeType(long userId, int dataScopeType)
        {
            var cacheKey = CacheConst.KeyMaxDataScopeType + userId;
            await SetStringAsync(cacheKey, dataScopeType.ToString());

            await AddCacheKeyAsync(cacheKey);
        }
        #endregion

        #region Del
        public async Task DelByParentKeyAsync(string key)
        {
            var allkeys = await GetAllCacheKeysAsync();
            if (allkeys == null) return;

            var delAllkeys = allkeys.Where(u => u.StartsWith(key)).ToList();
            delAllkeys.ForEach(Remove);
            // 更新所有缓存键
            allkeys = allkeys.Where(u => !u.StartsWith(key)).ToList();
            await SetStringAsync(CacheConst.KeyAll, allkeys.ToJson());
        }

        public void DelByPattern(string key)
        {
            var allkeys = GetAllCacheKeys();
            if (allkeys == null) return;

            var delAllkeys = allkeys.Where(u => u.Contains(key)).ToList();
            delAllkeys.ForEach(u => { _cache.Remove(u); });

            // 更新所有缓存键
            allkeys = allkeys.Where(u => !u.Contains(key)).ToList();
            _cache.SetString(CacheConst.KeyAll, allkeys.ToJson());
        }

        public async Task DelByPatternAsync(string key)
        {
            var allkeys = await GetAllCacheKeysAsync();
            if (allkeys == null) return;

            var delAllkeys = allkeys.Where(u => u.Contains(key)).ToList();
            delAllkeys.ForEach(u => { _cache.Remove(u); });

            // 更新所有缓存键
            allkeys = allkeys.Where(u => !u.Contains(key)).ToList();
            await _cache.SetStringAsync(CacheConst.KeyAll, allkeys.ToJson());
        }

        public void DelCacheKey(string cacheKey)
        {
            var res = _cache.GetString(CacheConst.KeyAll);
            var allkeys = string.IsNullOrWhiteSpace(res) ? new List<string>() : res.ToObject<List<string>>();
            if (allkeys.Any(m => m == cacheKey))
            {
                allkeys.Remove(cacheKey);
                _cache.SetString(CacheConst.KeyAll, allkeys.ToJson());
            }
        }

        public async Task DelCacheKeyAsync(string cacheKey)
        {
            var res = await _cache.GetStringAsync(CacheConst.KeyAll);
            var allkeys = string.IsNullOrWhiteSpace(res) ? new List<string>() : res.ToObject<List<string>>();
            if (allkeys.Any(m => m == cacheKey))
            {
                allkeys.Remove(cacheKey);
                await _cache.SetStringAsync(CacheConst.KeyAll, allkeys.ToJson());
            }
        } 
        #endregion
    }
}
